
package net.lab0.nebula.gui;


import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;

import javax.swing.JPanel;

import net.lab0.nebula.data.Statistics;
import net.lab0.nebula.data.StatisticsData;
import net.lab0.nebula.enums.Status;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.PiePlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.renderer.category.CategoryItemRenderer;
import org.jfree.chart.title.TextTitle;
import org.jfree.data.category.CategoryDataset;
import org.jfree.data.category.DefaultCategoryDataset;
import org.jfree.data.general.DatasetUtilities;
import org.jfree.data.general.DefaultPieDataset;

import com.flaptor.hist4j.AdaptiveHistogram;


public class ChartsFactory
{
    private static Font titleFont = new Font("sansserif", Font.PLAIN, 16);
    
    public static JPanel createGeneralStatisticsPanel(Statistics statistics)
    {
        int maxDepth = statistics.getMaxDepth();
        
        JPanel panel = new JPanel();
        panel.setLayout(new GridBagLayout());
        GridBagConstraints gridBagConstraints = new GridBagConstraints();
        gridBagConstraints.fill = GridBagConstraints.HORIZONTAL;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.gridy = 0;
        
        JPanel graphPanel1 = createGeneralCountPerDepthGraph(statistics, maxDepth);
        JPanel graphPanel2 = createGeneralSurfaceGraph(statistics, maxDepth);
        JPanel graphPanel3 = createGeneralStackedSurfaceGraph(statistics, maxDepth);

        panel.add(graphPanel1, gridBagConstraints);
        gridBagConstraints.gridy++;
        panel.add(graphPanel2, gridBagConstraints);
        gridBagConstraints.gridy++;
        panel.add(graphPanel3, gridBagConstraints);
        gridBagConstraints.gridy++;
        
        return panel;
    }
    
    private static ChartPanel createGeneralStackedSurfaceGraph(Statistics statistics, int maxDepth)
    {
        
        double[][] data = new double[Status.values().length][statistics.getMaxDepth()];
        
        String[] seriesNames = new String[Status.values().length];
        String[] seriesDepth = new String[statistics.getMaxDepth()];
        for (int i = 0; i < statistics.getMaxDepth(); ++i)
        {
            seriesDepth[i] = "" + i;
        }
        for (Status status : Status.values())
        {
            seriesNames[status.ordinal()] = status.toString();
        }
        
        double insideSurface = 0;
        double outsideSurface = 0;
        double voidSurface = 0;
        for (int i = 0; i < maxDepth; ++i)
        {
            for (Status status : Status.values())
            {
                double quantity = statistics.getStatisticsDataForDepth(i).getSurfaceFor(status);
                
                switch (status)
                {
                    case BROWSED:
                        data[status.ordinal()][i] = quantity;
                        break;
                    
                    case INSIDE:
                        insideSurface += quantity;
                        data[status.ordinal()][i] = insideSurface;
                        break;
                    
                    case OUTSIDE:
                        outsideSurface += quantity;
                        data[status.ordinal()][i] = outsideSurface;
                        break;
                    
                    case VOID:
                        voidSurface += quantity;
                        data[status.ordinal()][i] = voidSurface;
                        break;
                    
                    default:
                        break;
                }
            }
        }
        
        CategoryDataset categoryDataset = DatasetUtilities.createCategoryDataset(seriesNames, seriesDepth, data);
        
        final JFreeChart chart = ChartFactory.createStackedAreaChart("Surface", "Depth", "Surface", categoryDataset, PlotOrientation.VERTICAL, true, true,
        false);
        applyColorsTo1Series(chart);
        applyFontFormatting(chart);
        return new ChartPanel(chart);
    }
    
    private static ChartPanel createGeneralSurfaceGraph(Statistics statistics, int maxDepth)
    {
        double[][] data = new double[Status.values().length][statistics.getMaxDepth()];
        
        String[] seriesNames = new String[Status.values().length];
        String[] seriesDepth = new String[statistics.getMaxDepth()];
        for (int i = 0; i < statistics.getMaxDepth(); ++i)
        {
            seriesDepth[i] = "" + i;
        }
        for (Status status : Status.values())
        {
            seriesNames[status.ordinal()] = status.toString();
        }
        
        for (int i = 0; i < maxDepth; ++i)
        {
            for (Status status : Status.values())
            {
                double quantity = statistics.getStatisticsDataForDepth(i).getSurfaceFor(status);
                data[status.ordinal()][i] = quantity;
            }
        }
        
        CategoryDataset categoryDataset = DatasetUtilities.createCategoryDataset(seriesNames, seriesDepth, data);
        
        final JFreeChart chart = ChartFactory.createStackedAreaChart("Surface", "Depth", "Surface", categoryDataset, PlotOrientation.VERTICAL, true, true,
        false);
        applyColorsTo1Series(chart);
        applyFontFormatting(chart);
        return new ChartPanel(chart);
    }
    
    private static ChartPanel createGeneralCountPerDepthGraph(Statistics statistics, int maxDepth)
    {
        double[][] data = new double[Status.values().length][statistics.getMaxDepth()];
        
        String[] seriesNames = new String[Status.values().length];
        String[] seriesDepth = new String[statistics.getMaxDepth()];
        for (int i = 0; i < statistics.getMaxDepth(); ++i)
        {
            seriesDepth[i] = "" + i;
        }
        for (Status status : Status.values())
        {
            seriesNames[status.ordinal()] = status.toString();
        }
        
        for (int i = 0; i < maxDepth; ++i)
        {
            for (Status status : Status.values())
            {
                double quantity = statistics.getStatisticsDataForDepth(i).getCountFor(status);
                data[status.ordinal()][i] = quantity;
            }
        }
        
        CategoryDataset categoryDataset = DatasetUtilities.createCategoryDataset(seriesNames, seriesDepth, data);
        
        final JFreeChart countChart = ChartFactory.createStackedAreaChart("Count per depth", "Depth", "Count", categoryDataset, PlotOrientation.VERTICAL, true,
        true, false);
        applyColorsTo1Series(countChart);
        applyFontFormatting(countChart);
        
        return new ChartPanel(countChart);
    }
    
    private static void applyFontFormatting(JFreeChart chart)
    {
        TextTitle title = chart.getTitle();
        title.setFont(titleFont);
    }
    
    private static void applySizeFormatting(ChartPanel chart)
    {
        chart.setPreferredSize(new Dimension(512, 512));
    }
    
    private static void applyColorsTo1Series(final JFreeChart perDepthCountChart)
    {
        CategoryItemRenderer renderer = perDepthCountChart.getCategoryPlot().getRenderer();
        renderer.setSeriesPaint(0, Color.GREEN);
        renderer.setSeriesPaint(1, Color.BLUE);
        renderer.setSeriesPaint(2, Color.RED);
        renderer.setSeriesPaint(3, Color.WHITE);
    }
    
    public static JPanel createPerLayerGraph(Statistics statistics)
    {
        JPanel panel = new JPanel();
        panel.setLayout(new GridBagLayout());
        
        GridBagConstraints gridBagConstraints = new GridBagConstraints();
        gridBagConstraints.fill = GridBagConstraints.HORIZONTAL;
        gridBagConstraints.weightx = 1.0;
        System.out.println("max depth " + statistics.getMaxDepth());
        for (int i = 0; i < statistics.getMaxDepth(); ++i)
        {
            gridBagConstraints.gridy = i;
            StatisticsData statData = statistics.getStatisticsDataForDepth(i);
            
            DefaultPieDataset countDataset = new DefaultPieDataset();
            for (Status status : Status.values())
            {
                countDataset.setValue(status.toString(), statData.getCountFor(status));
            }
            
            DefaultCategoryDataset categoryDataset = new DefaultCategoryDataset();
            AdaptiveHistogram adaptiveHistogram = statData.getOutsideIterationHitogram();
            
            int iter = 0;
            while (iter < statistics.getMaxKnownIter())
            {
                categoryDataset.addValue(adaptiveHistogram.getAccumCount(iter + 0.5f) - adaptiveHistogram.getAccumCount(iter - 0.5f), "Frequency", "" + iter);
                iter++;
            }
            
            JFreeChart countChart = ChartFactory.createPieChart("Count@" + i, countDataset, true, true, false);
            formatChart(countChart);
            applyFontFormatting(countChart);
            
            JFreeChart frequencyChart = ChartFactory.createBarChart("Iterations frequency", "Iterations", "Amount", categoryDataset, PlotOrientation.VERTICAL,
            true, false, false);
            applyFontFormatting(frequencyChart);
            
            ChartPanel countChartPanel = new ChartPanel(countChart);
            ChartPanel frequencyChartPanel = new ChartPanel(frequencyChart);
            
            applySizeFormatting(countChartPanel);
            applySizeFormatting(frequencyChartPanel);
            
            gridBagConstraints.gridx = 0;
            panel.add(countChartPanel, gridBagConstraints);
            gridBagConstraints.gridx = 1;
            panel.add(frequencyChartPanel, gridBagConstraints);
        }
        
        return panel;
    }

    private static void formatChart(JFreeChart surfaceChart)
    {
        PiePlot plot = (PiePlot) surfaceChart.getPlot();
        plot.setLabelFont(new Font("SansSerif", Font.PLAIN, 12));
        plot.setNoDataMessage("No data available");
        plot.setCircular(false);
        plot.setLabelGap(0.02);
        
        plot.setSectionPaint(Status.BROWSED.toString(), Color.GREEN);
        plot.setSectionPaint(Status.INSIDE.toString(), Color.BLUE);
        plot.setSectionPaint(Status.OUTSIDE.toString(), Color.RED);
        plot.setSectionPaint(Status.VOID.toString(), Color.WHITE);
    }
}
